package com.sinsz.pay.factory.alipay;

import com.alipay.api.AlipayApiException;
import com.alipay.api.domain.*;
import com.alipay.api.request.*;
import com.alipay.api.response.*;
import com.sinsz.common.exception.ApiException;
import com.sinsz.pay.dto.*;
import com.sinsz.pay.factory.BasePay;
import com.sinsz.pay.factory.support.BillType;
import com.sinsz.pay.factory.support.MergeStatus;
import com.sinsz.pay.factory.support.SSLConstant;
import com.sinsz.pay.properties.PayProperties;
import com.sinsz.pay.support.AliPayConstant;
import com.sinsz.pay.support.Constant;
import com.sinsz.pay.util.PayUtils;
import org.apache.commons.lang3.StringUtils;
import org.nutz.http.Http;
import org.nutz.http.Response;
import org.nutz.json.Json;
import org.nutz.json.JsonFormat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;

import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 支付宝支付实现
 * @author chenjianbo
 */
public abstract class BaseAlipayImpl extends BasePay {

    private PayProperties prop;
    private HttpServletRequest request;

    private static final Logger logger = LoggerFactory.getLogger(BaseAlipayImpl.class);

    BaseAlipayImpl(PayProperties prop, HttpServletRequest request) {
        super(prop);
        this.prop = super.getProp();
        this.request = request;
    }

    /**
     * 统一下单，预支付创建
     * @param outTradeNo    自定义商户订单号
     * @param body          主描述信息
     * @param detail        描述信息
     * @param fee           费用，精度：分
     * @param openid        公众号时为公众号唯一的用户ID
     * @return              支付串
     */
    @Override
    public abstract String unifiedOrder(String outTradeNo, String body, String detail, int fee, String openid);

    @Override
    public String orderQuery(String transactionId, String outTradeNo) {
        outTradeNo = formatOutTradeNo(outTradeNo);
        AlipayTradeQueryModel model=new AlipayTradeQueryModel();
        model.setOutTradeNo(outTradeNo);
        if (!StringUtils.isEmpty(transactionId)) {
            model.setTradeNo(transactionId);
        }
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        request.setBizModel(model);
        try {
            AlipayTradeQueryResponse response = SSLConstant.instance().alipayClient().execute(request);
            OrderQueryDto queryDto = new OrderQueryDto(
                    MergeStatus.aliPayStatus(response.getTradeStatus()).name(),
                    response.getTotalAmount(),
                    response.getTradeNo(),
                    response.getOutTradeNo(),
                    response.getSendPayDate() == null ? "" : new SimpleDateFormat("yyyyMMddHHmmss").format(response.getSendPayDate()),
                    MergeStatus.aliPayStatus(response.getTradeStatus()).getDesc()
            );
            if (!response.isSuccess()) {
                logger.error("失败信息描述：" + Json.toJson(response, JsonFormat.tidy()));
            }
            return Json.toJson(queryDto, JsonFormat.tidy());
        } catch (AlipayApiException e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,"支付查询未知异常.");
        }
    }

    @Override
    public String closeOrder(String outTradeNo) {
        outTradeNo = formatOutTradeNo(outTradeNo);
        AlipayTradeCloseModel model = new AlipayTradeCloseModel();
        model.setOutTradeNo(outTradeNo);
        AlipayTradeCloseRequest request=new AlipayTradeCloseRequest();
        request.setBizModel(model);
        try {
            AlipayTradeCloseResponse response = SSLConstant.instance().alipayClient().execute(request);
            if (response.isSuccess()) {
                return AliPayConstant.OK;
            } else {
                logger.error("失败信息描述：" + Json.toJson(response, JsonFormat.tidy()));
                return AliPayConstant.FAIL;
            }
        } catch (AlipayApiException e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,"支付关闭未知异常.");
        }
    }

    @Override
    public String refund(String transactionId, String outTradeNo, String outRefundNo, int totalFee, int fee, String refundDesc) {
        outTradeNo = formatOutTradeNo(outTradeNo);
        outRefundNo = formatOutRefundNo(outRefundNo);
        totalFee = formatFee(totalFee);
        fee = formatRefundFee(totalFee, fee);
        refundDesc = StringUtils.isEmpty(refundDesc) ? "退款": refundDesc;
        AlipayTradeRefundModel model=new AlipayTradeRefundModel();
        model.setOutTradeNo(outTradeNo);
        if (!StringUtils.isEmpty(transactionId)) {
            model.setTradeNo(transactionId);
        }
        model.setRefundAmount(formatMoney(fee));
        model.setRefundReason(refundDesc);
        model.setOutRequestNo(outRefundNo);
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        request.setBizModel(model);
        if (!StringUtils.isEmpty(prop.getAlipay().getRefundNotify())) {
            request.setNotifyUrl(PayUtils.redirect(prop.getHttp(), prop.getAlipay().getRefundNotify(), false));
        }
        try {
            AlipayTradeRefundResponse response = SSLConstant.instance().alipayClient().execute(request);
            if (response.isSuccess()) {
                RefundDto refundDto = new RefundDto(
                        response.getTradeNo(),
                        response.getOutTradeNo(),
                        outRefundNo,
                        "",
                        System.currentTimeMillis()
                );
                return Json.toJson(refundDto, JsonFormat.tidy());
            } else {
                throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,Json.toJson(response, JsonFormat.tidy()));
            }
        } catch (AlipayApiException e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,"支付退款申请未知异常.");
        }
    }

    @Override
    public String refundQuery(String outTradeNo, String outRefundNo) {
        outTradeNo = formatOutTradeNo(outTradeNo);
        outRefundNo = formatOutRefundNo(outRefundNo);
        AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
        model.setOutTradeNo(outTradeNo);
        model.setOutRequestNo(outRefundNo);
        AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
        request.setBizModel(model);
        try {
            AlipayTradeFastpayRefundQueryResponse response = SSLConstant.instance().alipayClient().execute(request);
            if (response.isSuccess()) {
                RefundQueryDto refundQueryDto = new RefundQueryDto(
                        response.getTradeNo(),
                        response.getOutTradeNo(),
                        outRefundNo,
                        "",
                        1,
                        MergeStatus.aliRefundStatus().name(),
                        response.getGmtRefundPay() == null ? "" : new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(response.getGmtRefundPay())
                );
                return Json.toJson(refundQueryDto, JsonFormat.tidy());
            } else {
                throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,Json.toJson(response, JsonFormat.tidy()));
            }
        } catch (AlipayApiException e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,"支付退款查询未知异常.");
        }
    }

    @Override
    public ResponseEntity<InputStreamResource> downloadBill(Date date, BillType billType) {
        String billDate = formatAliBillDate(date);
        billType = billType == null ? BillType.ALL: billType;
        switch (billType) {
            case TRADE:
            case SIGNCUSTOMER:
                break;
            default:
                billType = BillType.TRADE;
        }
        AlipayDataDataserviceBillDownloadurlQueryModel model = new AlipayDataDataserviceBillDownloadurlQueryModel();
        model.setBillDate(billDate);
        model.setBillType(billType.name().toLowerCase());
        AlipayDataDataserviceBillDownloadurlQueryRequest request = new AlipayDataDataserviceBillDownloadurlQueryRequest();
        request.setBizModel(model);
        try {
            AlipayDataDataserviceBillDownloadurlQueryResponse response = SSLConstant.instance().alipayClient().execute(request);
            if (response.isSuccess()) {
                Response resp = Http.get(response.getBillDownloadUrl());
                InputStream is = resp.getStream();
                int len = is.available();
                if (len <= 0) {
                    throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,Json.toJson(response, JsonFormat.tidy()));
                }
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
                headers.setCacheControl("no-cache, no-store, must-revalidate");
                headers.setContentDispositionFormData("attachment", "ali" + formatBillDate(date) + ".zip");
                headers.setPragma("no-cache");
                headers.setExpires(0L);
                return ResponseEntity
                        .ok()
                        .headers(headers)
                        .contentLength(len)
                        .contentType(MediaType.APPLICATION_OCTET_STREAM)
                        .body(new InputStreamResource(is));
            } else {
                throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,Json.toJson(response, JsonFormat.tidy()));
            }
        } catch (Exception e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,"支付宝支付下载对账单异常");
        }
    }

    @Override
    public String payNotify() {
        try {
            String outTradeNo = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
            String tradeNo = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
            return orderQuery(tradeNo, outTradeNo);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,"支付结果回调异常.");
        }
    }

    @Override
    public String refundNotify() {
        try {
            String outTradeNo = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
            String outBizNo = new String(request.getParameter("out_biz_no").getBytes("ISO-8859-1"),"UTF-8");
            return refundQuery(outTradeNo, outBizNo);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,"支付退款结果回调异常.");
        }
    }

    @Override
    public String transfers(String partnerTradeNo, String userName, int amount, String desc, String openid) {
        partnerTradeNo = formatPartnerTradeNo(partnerTradeNo);
        userName = formatUserName(userName);
        openid = formatOpenid(openid);
        amount = formatTransferFee(amount);
        String money = formatMoney(amount);
        desc = StringUtils.isEmpty(desc) ? "平台转账" : desc;
        AlipayFundTransToaccountTransferModel model = new AlipayFundTransToaccountTransferModel();
        model.setOutBizNo(partnerTradeNo);
        model.setPayeeType(AliPayConstant.PAYEE_TYPE);
        model.setPayeeAccount(openid);
        model.setAmount(money);
        model.setPayeeRealName(userName);
        model.setRemark(desc);
        AlipayFundTransToaccountTransferRequest request = new AlipayFundTransToaccountTransferRequest();
        request.setBizModel(model);
        try {
            AlipayFundTransToaccountTransferResponse response = SSLConstant.instance().alipayClient().execute(request);
            if (response.isSuccess()) {
                TransferDto transferDto = new TransferDto();
                transferDto.setPartnerTradeNo(response.getOutBizNo());
                transferDto.setPaymentNo(response.getOrderId());
                transferDto.setPaymentTime(response.getPayDate());
                return Json.toJson(transferDto, JsonFormat.tidy());
            } else {
                throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,Json.toJson(response, JsonFormat.tidy()));
            }
        } catch (AlipayApiException e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,"付款异常.");
        }
    }

    @Override
    public String getTransferInfo(String partnerTradeNo) {
        partnerTradeNo = formatPartnerTradeNo(partnerTradeNo);
        AlipayFundTransOrderQueryModel model = new AlipayFundTransOrderQueryModel();
        model.setOutBizNo(partnerTradeNo);
        AlipayFundTransOrderQueryRequest request = new AlipayFundTransOrderQueryRequest();
        request.setBizModel(model);
        try {
            AlipayFundTransOrderQueryResponse response = SSLConstant.instance().alipayClient().execute(request);
            TransferQueryDto queryDto = new TransferQueryDto();
            queryDto.setPartnerTradeNo(response.getOutBizNo());
            queryDto.setPaymentNo(response.getOrderId());
            queryDto.setStatus(MergeStatus.aliTransferStatus(response.getStatus()).name());
            queryDto.setReason(StringUtils.isEmpty(response.getFailReason()) ? MergeStatus.aliTransferStatus(response.getStatus()).getDesc(): response.getFailReason());
            queryDto.setOpenid("");
            queryDto.setUserName("");
            queryDto.setFee("");
            queryDto.setTransferTime(response.getPayDate());
            queryDto.setPaymentTime(response.getArrivalTimeEnd());
            queryDto.setDesc(response.getFailReason());
            if (!response.isSuccess()) {
                logger.error("失败信息描述：" + Json.toJson(response, JsonFormat.tidy()));
            }
            return Json.toJson(queryDto, JsonFormat.tidy());
        } catch (AlipayApiException e) {
            e.printStackTrace(System.out);
            throw new ApiException(Constant.DEFAULT_EXCEPTION_CODE,"查询付款异常.");
        }
    }

}
